from sqlalchemy import select, update, delete, func, and_
from sqlalchemy.ext.asyncio import AsyncSession
from datetime import date

from module_kylin.entity.do.future_account_daily_do import KylinFutureAccountDaily
from module_kylin.entity.vo.future_account_daily_vo import FutureAccountDailyPageQueryModel, FutureAccountDailyModel

from utils.page_util import PageUtil


class FutureAccountDailyDao:
    """
    期货账户资金日报数据访问层
    """

    @classmethod
    async def add_account_daily(cls, db: AsyncSession, account_daily: FutureAccountDailyModel):
        """
        新增期货账户资金日报

        :param db: orm对象
        :param account_daily: 账户资金日报信息
        :return: 操作结果
        """
        # 记录DAO层收到的字段值
        import logging
        import numpy as np
        logger = logging.getLogger(__name__)
        
        # 定义处理NaN值的函数
        def handle_nan_and_null(value, default=0):
            if value is None or (isinstance(value, float) and np.isnan(value)):
                logger.warning(f'发现NaN或None值，替换为默认值: {default}')
                return default
            return value
        
        # 首先保存关键值到局部变量，避免在方法执行过程中被修改
        account_code = account_daily.account_code
        settlement_date = account_daily.settlement_date
        
        # DAO层字段值接收日志 - 增加更详细的调试信息
        logger.info(f'DAO层添加方法 - 接收到的account_code: {account_code}, 类型: {type(account_code)}, settlement_date: {settlement_date}')
        
        # 验证必填字段 - 添加更详细的日志
        if account_code is None:
            logger.critical('DAO层验证失败: account_code为None')
            from exceptions.exception import ServiceException
            raise ServiceException('账户代码为空，导入失败')
        if account_code == '':
            logger.critical('DAO层验证失败: account_code为空字符串')
            from exceptions.exception import ServiceException
            raise ServiceException('账户代码为空，导入失败')
        if not account_code:
            logger.critical(f'DAO层验证失败: account_code值为: {account_code}')
            from exceptions.exception import ServiceException
            raise ServiceException('账户代码为空，导入失败')
        
        if not settlement_date:
            logger.critical('DAO层验证失败: settlement_date为空')
            from exceptions.exception import ServiceException
            raise ServiceException('结算日期为空，导入失败')
            
        # 创建账户资金日报对象，使用局部变量而不是直接引用对象属性，并处理NaN值
        account_daily_do = KylinFutureAccountDaily(
            account_code=account_code,
            settlement_date=settlement_date,
            previous_balance_day=handle_nan_and_null(account_daily.previous_balance_day),
            previous_balance_trade=handle_nan_and_null(account_daily.previous_balance_trade),
            daily_deposit_withdrawal=handle_nan_and_null(account_daily.daily_deposit_withdrawal),
            daily_profit_day=handle_nan_and_null(account_daily.daily_profit_day),
            daily_balance_day=handle_nan_and_null(account_daily.daily_balance_day),
            close_profit_trade=handle_nan_and_null(account_daily.close_profit_trade),
            daily_balance_trade=handle_nan_and_null(account_daily.daily_balance_trade),
            float_profit_trade=handle_nan_and_null(account_daily.float_profit_trade),
            daily_fee=handle_nan_and_null(account_daily.daily_fee),
            equity=handle_nan_and_null(account_daily.equity),
            margin_used=handle_nan_and_null(account_daily.margin_used),
            available_funds=handle_nan_and_null(account_daily.available_funds),
            risk_ratio=handle_nan_and_null(account_daily.risk_ratio)
        )
        
        # 再次验证创建的数据库对象字段值
        logger.info(f'DAO层创建数据库对象 - account_code: {account_daily_do.account_code}, settlement_date: {account_daily_do.settlement_date}')
        
        db.add(account_daily_do)
        await db.flush()
        
        logger.info(f'DAO层添加操作成功完成')
        return account_daily_do

    @classmethod
    def _transform_account_daily_item(cls, item):
        """
        将数据库对象转换为字典，保持数据库字段名称一致
        
        :param item: 数据库查询返回的对象或字典
        :return: 转换后的字典对象
        """
        if not item:
            return None
        
        # 检查是否已经是字典类型
        if isinstance(item, dict):
            return item
            
        # 直接使用数据库字段名称
        return {
            'id': item.id,
            'account_code': item.account_code,
            'settlement_date': item.settlement_date,
            'previous_balance_day': item.previous_balance_day,
            'previous_balance_trade': item.previous_balance_trade,
            'daily_deposit_withdrawal': item.daily_deposit_withdrawal,
            'daily_profit_day': item.daily_profit_day,
            'daily_balance_day': item.daily_balance_day,
            'close_profit_trade': item.close_profit_trade,
            'daily_balance_trade': item.daily_balance_trade,
            'float_profit_trade': item.float_profit_trade,
            'daily_fee': item.daily_fee,
            'equity': item.equity,
            'margin_used': item.margin_used,
            'available_funds': item.available_funds,
            'risk_ratio': item.risk_ratio,
            'create_time': item.create_time,
            'update_time': item.update_time
        }
        
    @classmethod
    async def get_account_daily_by_id(cls, db: AsyncSession, daily_id: int):
        """
        根据ID获取期货账户资金日报信息

        :param db: orm对象
        :param daily_id: 账户资金日报id
        :return: 期货账户资金日报信息对象
        """
        account_daily_info = (
            (await db.execute(select(KylinFutureAccountDaily).where(KylinFutureAccountDaily.id == daily_id)))
            .scalars()
            .first()
        )

        # 转换字段名称以匹配前端期望
        return cls._transform_account_daily_item(account_daily_info)
        
    @classmethod
    async def get_account_daily_by_date_and_account(
        cls, db: AsyncSession, settlement_date: date, account_code: str
    ):
        """
        根据结算日期和账户代码获取账户资金日报信息

        :param db: orm对象
        :param settlement_date: 结算日期
        :param account_code: 账户代码
        :return: 期货账户资金日报信息对象
        """
        result = await db.execute(
            select(KylinFutureAccountDaily)
            .where(KylinFutureAccountDaily.settlement_date == settlement_date)
            .where(KylinFutureAccountDaily.account_code == account_code)
        )
        
        account_daily_info = result.scalars().first()
        
        # 转换字段名称以匹配前端期望
        return cls._transform_account_daily_item(account_daily_info)

    @classmethod
    async def get_account_daily_list(
        cls, db: AsyncSession, query_model: FutureAccountDailyPageQueryModel, is_page: bool = True
    ):
        """
        获取期货账户资金日报列表

        :param db: orm对象
        :param query_model: 查询参数对象
        :param is_page: 是否开启分页
        :return: 期货账户资金日报列表
        """
        query = select(KylinFutureAccountDaily)

        if query_model.account_code:
            query = query.where(KylinFutureAccountDaily.account_code == query_model.account_code)

        # 日期段查询：开始日期
        if hasattr(query_model, 'begin_date') and query_model.begin_date:
            query = query.where(KylinFutureAccountDaily.settlement_date >= query_model.begin_date)

        # 日期段查询：结束日期
        if hasattr(query_model, 'end_date') and query_model.end_date:
            query = query.where(KylinFutureAccountDaily.settlement_date <= query_model.end_date)

        # 按结算日期降序、账户代码升序排序
        query = query.order_by(
            KylinFutureAccountDaily.settlement_date.desc(),
            KylinFutureAccountDaily.account_code
        )

        # 构建分页查询
        account_daily_page = await PageUtil.paginate(db, query, query_model.page_num, query_model.page_size, is_page)

        # 转换字段名称以匹配前端期望
        if is_page:
            # 参考futureAccount模块的实现，使用正确的分页属性
            if hasattr(account_daily_page, 'items'):
                account_daily_page.items = [cls._transform_account_daily_item(item) for item in account_daily_page.items]
                # 确保响应结构符合前端期望
                result = {
                    'rows': account_daily_page.items,
                    'total': account_daily_page.total
                }
                return result
            else:
                # 兼容rows属性的情况
                account_daily_page.rows = [cls._transform_account_daily_item(item) for item in getattr(account_daily_page, 'rows', [])]
                return account_daily_page
        else:
            # 处理不分页数据 - 单个记录时进行转换
            if account_daily_page and not isinstance(account_daily_page, list):
                return cls._transform_account_daily_item(account_daily_page)
            # 导出时使用的列表数据也进行转换
            elif isinstance(account_daily_page, list):
                return [cls._transform_account_daily_item(item) for item in account_daily_page]
            return account_daily_page

    @classmethod
    async def update_account_daily(cls, db: AsyncSession, daily_id: int, account_daily: FutureAccountDailyModel):
        """
        更新期货账户资金日报

        :param db: orm对象
        :param daily_id: 账户资金日报id
        :param account_daily: 期货账户资金日报对象
        :return: 操作结果
        """
        update_data = {}
        if account_daily.account_code is not None:
            update_data['account_code'] = account_daily.account_code
        if account_daily.settlement_date is not None:
            update_data['settlement_date'] = account_daily.settlement_date
        if account_daily.previous_balance_day is not None:
            update_data['previous_balance_day'] = account_daily.previous_balance_day
        if account_daily.previous_balance_trade is not None:
            update_data['previous_balance_trade'] = account_daily.previous_balance_trade
        if account_daily.daily_deposit_withdrawal is not None:
            update_data['daily_deposit_withdrawal'] = account_daily.daily_deposit_withdrawal
        if account_daily.daily_profit_day is not None:
            update_data['daily_profit_day'] = account_daily.daily_profit_day
        if account_daily.daily_balance_day is not None:
            update_data['daily_balance_day'] = account_daily.daily_balance_day
        if account_daily.close_profit_trade is not None:
            update_data['close_profit_trade'] = account_daily.close_profit_trade
        if account_daily.daily_balance_trade is not None:
            update_data['daily_balance_trade'] = account_daily.daily_balance_trade
        if account_daily.float_profit_trade is not None:
            update_data['float_profit_trade'] = account_daily.float_profit_trade
        if account_daily.daily_fee is not None:
            update_data['daily_fee'] = account_daily.daily_fee
        if account_daily.equity is not None:
            update_data['equity'] = account_daily.equity
        if account_daily.margin_used is not None:
            update_data['margin_used'] = account_daily.margin_used
        if account_daily.available_funds is not None:
            update_data['available_funds'] = account_daily.available_funds
        if account_daily.risk_ratio is not None:
            update_data['risk_ratio'] = account_daily.risk_ratio
            
        # 如果有数据需要更新
        if update_data:
            await db.execute(
                update(KylinFutureAccountDaily).where(KylinFutureAccountDaily.id == daily_id).values(**update_data)
            )
            await db.flush()
        
        return True

    @classmethod
    async def delete_account_daily(cls, db: AsyncSession, daily_id: int):
        """
        删除期货账户资金日报

        :param db: orm对象
        :param daily_id: 账户资金日报id
        :return: 操作结果
        """
        await db.execute(delete(KylinFutureAccountDaily).where(KylinFutureAccountDaily.id == daily_id))
        await db.flush()
        return True

    @classmethod
    async def batch_delete_account_daily(cls, db: AsyncSession, daily_ids: list[int]):
        """
        批量删除期货账户资金日报

        :param db: orm对象
        :param daily_ids: 账户资金日报id列表
        :return: 操作结果
        """
        await db.execute(delete(KylinFutureAccountDaily).where(KylinFutureAccountDaily.id.in_(daily_ids)))
        await db.flush()
        return True
        
    @classmethod
    async def delete_account_daily_by_date_and_account(cls, db: AsyncSession, settlement_date: date, account_code: str):
        """
        根据结算日期和账户代码删除期货账户资金日报

        :param db: orm对象
        :param settlement_date: 结算日期
        :param account_code: 账户代码
        :return: 操作结果
        """
        result = await db.execute(
            delete(KylinFutureAccountDaily)
            .where(KylinFutureAccountDaily.settlement_date == settlement_date)
            .where(KylinFutureAccountDaily.account_code == account_code)
        )
        await db.flush()
        return result.rowcount > 0  # 返回是否有记录被删除